package com.ht.api.controller;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.ht.api.bean.AjaxResult;
import com.ht.api.bean.LoginBean;
import com.ht.api.bean.UserInfo;
import com.ht.api.call.ICall;
import com.ht.api.db.Db;
import com.ht.api.db.DbConfig;
import com.ht.api.db.DbList;
import com.ht.api.db.Entity;
import com.ht.api.db.SqlParser;
import com.ht.api.db.exception.HtException;
import com.ht.api.db.util.HtUtil;
import com.ht.api.util.UserUtil;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONException;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

/**
 * 通用增删改查接口
 * @author asq
 * @createTime 2025年1月3日 12:03:59
 */
@RestController
@RequestMapping("/common")
public class CommonController {
	/** 新增 */
	private static final String ADD = "add";
	/** 批量新增 */
	private static final String BATCH = "batch";
	/** 修改 */
	private static final String EDIT = "edit";
	/** 删除 */
	private static final String DEL = "del";
	/** 查找单个 */
	private static final String FIND = "find";
	/** 查询 */
	private static final String QUERY = "query";
	/** 允许的操作 */
	private static final List<String> OPTS = Arrays.asList(ADD, BATCH, EDIT, DEL, FIND, QUERY);
	/** 不允许操作的系统表名 */
	private static final List<String> SYS_TABLES = Arrays.asList("db_table", "db_column");
	
	/** json参数中的main_id路径 */
	private String path = String.format("%s.main_id", ICall.PARAM);
	
	@Autowired
	private Map<String, ICall> callMap;

	/**
	 * 通用增删改查（使用租户数据源）
	 * @param table 表名
	 * @param opt 操作类型
	 * @param body 接口参数
	 * @return
	 */
	@PostMapping("/{table}/{opt}")
	public AjaxResult executeByTenant(@PathVariable String table, @PathVariable String opt, @RequestBody JSONObject body) {
		// 获取登录用户
		UserInfo loginUser = UserUtil.getLoginUser();
		long loginId = loginUser.getId();
		long tenantId = loginUser.getTenantId();
		
		return doExecute(LoginBean.createByTenant(loginId, tenantId), table, opt, body);
	}

	/**
	 * 通用增删改查（使用系统数据源）
	 * @param table 表名
	 * @param opt 操作类型
	 * @param body 接口参数
	 * @return
	 */
	@PostMapping("/sys/{table}/{opt}")
	public AjaxResult executeBySys(@PathVariable String table, @PathVariable String opt, @RequestBody JSONObject body) {
		// 获取登录用户
		UserInfo loginUser = UserUtil.getLoginUser();
		long loginId = loginUser.getId();
		
		return doExecute(LoginBean.createBySys(loginId), table, opt, body);
	}
	
	/**
	 * 通用增删改查，调用ICall接口类，重用代码
	 * @param me 操作人
	 * @param table 表别名
	 * @param opt 操作类型
	 * @param body json参数对象
	 * @return
	 */
	private AjaxResult doExecute(LoginBean me, String table, String opt, JSONObject body) {
		if (!OPTS.contains(opt)) {
			throw new HtException(String.format("不支持的操作：[%s]", opt));
		}
		
		// 根据别名获取表名
		String tableCode = DbConfig.getAlias(table);
		
		// 验证参数
		checkParam(tableCode, opt, body);
		
		// 验证操作权限
		checkPerms(table, tableCode, opt);
		
		// 如果是查找单个，则直接调用Db方法查询，否则调用相应的Call接口
		if (FIND.equals(opt)) {
			String mainId = JSONUtil.getByPath(body, path, "");
			Entity entity = Db.createSqlParser(tableCode).findById(mainId);
			return AjaxResult.success().put(ICall.DATA, entity);
		} else {
			// 创建调用ICall接口的config参数（为了直接调用ICall接口）
			JSONObject config = JSONUtil.createObj().set("table", tableCode);
			
			// 根据操作类型，获取相应的ICall接口
			ICall call = callMap.get(String.format("%sCall", opt));
	
			// 调用具体接口
			return call.execute(me, body, config);
		}
	}

	/**
	 * 验证是否具有操作权限
	 * @param tableAlias 要操作的目标表的别名
	 * @param table 要操作的目标表的真名
	 * @param opt 要操作的类型
	 */
	private void checkPerms(String tableAlias, String table, String opt) {
		UserInfo login = UserUtil.getLoginUser();
		if (login.isAdmin()) {
			return ;
		}
		
		// 查询用户的角色列表
		StringBuilder sb = new StringBuilder();
		sb.append("select * from sys_role where main_id in(");
		sb.append("  SELECT role_id FROM `sys_user_role` where user_id=? and sys_deleted=1");
		sb.append(") and sys_deleted=1 and status=0");
		DbList roleList = Db.query(sb.toString(), login.getId());
		
		// 验证所有角色的权限配置，看是否允许当前操作
		String fieldPath = String.format("config.%s", getFieldByOpt(opt));
		for (Entity r : roleList) {
			try {
				String configStr = r.getStr("config");
				if (StrUtil.isNotBlank(configStr)) {
					JSONObject config = JSONUtil.parseObj(configStr);
					JSONArray tablePermArr = config.getJSONArray("table_perm");
					for (int i = 0; i < tablePermArr.size(); i++) {
						JSONObject tablePerm = tablePermArr.getJSONObject(i);
						if (table.equals(tablePerm.getStr("table"))) {
							boolean allow = JSONUtil.getByPath(tablePerm, fieldPath, false);
							if (allow) {
								return;
							}
						}
					}
				}
			} catch (JSONException e){
				throw new HtException(String.format("角色[%s]的config字段配置有误，请检查!", r.getStr("role_name")));
			}
		}
		
		// 如果所有角色都没包含当前操作权限，则直接拒绝
		throw new HtException(String.format("没有%s表的%s权限", tableAlias, opt));
	}

	/**
	 * 根据操作类型，返回权限规则中的json字段名
	 * @param opt 操作类型
	 * @return
	 */
	private String getFieldByOpt(String opt) {
		if (BATCH.equals(opt)) {
			return ADD;
		} else if (FIND.equals(opt)) {
			return QUERY;
		} else {
			return opt;
		}
	}

	/**
	 * 验证参数
	 * @param tableCode 表名
	 * @param opt 操作类型
	 * @param body 参数对象
	 */
	private void checkParam(String tableCode, String opt, JSONObject body) {
		// 不允许操作系统表：db_table和db_column，这两个表的操作使用MetaController
		if (SYS_TABLES.contains(tableCode)) {
			throw new HtException(String.format("不允许操作系统表：[%s]", tableCode));
		}
		
		// 所有操作要求参数必须要有param字段
		if (!body.containsKey(ICall.PARAM)) {
			throw new HtException(String.format("json参数需要%s字段~", ICall.PARAM));
		}
		
		// 新增时，不能有main_id
		if (ADD.equals(opt) && body.getByPath(path) != null) {
			throw new HtException(String.format("add操作不能有%s字段~", path));
		}
		
		// 查找单个数据时，必须要有main_id
		if (FIND.equals(opt) && body.getByPath(path) == null) {
			throw new HtException(String.format("find操作必须提供%s字段~", path));
		}
		
		// 修改，必须要有main_id
		if (EDIT.equals(opt) && body.getByPath(path) == null) {
			throw new HtException(String.format("edit操作必须提供%s字段~", path));
		}
		
		// 检查json参数的格式是否符合预期
		Class<?> clazz = null;
		if (QUERY.equals(opt)) {
			clazz = SqlParser.class;
		} else if (ADD.equals(opt) || EDIT.equals(opt)){
			clazz = Entity.class;
		}
		if (clazz != null && !HtUtil.canConvertToClass(body, ICall.PARAM, clazz)) {
			throw new HtException(String.format("json参数的%s字段格式不正确~", ICall.PARAM));
		}
	}
}
